/*
 * StreamLog.cpp
 *
 *  Created on: 2012-12-26
 *      Author: yangentao@gmail.com
 */
#include <windows.h>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <iomanip>
#include <boost/lexical_cast.hpp>
#include "StreamLog.h"
#include "Unicode.h"
#include "win32.h"
#include "Set.h"

using namespace std;
namespace yet {

LogMsg::LogMsg() {
	init();
}

void LogMsg::init() {
	GetSystemTime(&time);
	pid = GetCurrentProcessId();
	tid = GetCurrentThreadId();
	level = logInfo;
	tag = "";
	title = "";
	msg = "";
}
void LogMsg::setTimePidTid() {
	GetSystemTime(&time);
	pid = GetCurrentProcessId();
	tid = GetCurrentThreadId();
}

string LogMsg::toString() {
	stringstream ss;
	ss << setfill('0');
	ss << time.wYear << "-" << setw(2) << time.wMonth << "-" << setw(2) << time.wDay;
	ss << " " << setw(2) << time.wHour << ":" << setw(2) << time.wMinute << ":" << setw(2) << setfill('0') << time.wSecond;
	ss << ":" << setw(3) << setfill('0') << time.wMilliseconds;
	ss << " [" << setw(5) << pid << "][" << setw(5) << tid << "] ";
	switch (level) {
	case logVerbose:
		ss << "[VERBOSE]";
		break;
	case logWarn:
		ss << "[WARN]";
		break;
	case logError:
		ss << "[ERROR]";
		break;
	case logDebug:
		ss << "[DEBUG]";
		break;
	default: //info
		break;
	}
	ss << " ";
	if (!tag.empty()) {
		ss << "TAG:[" << tag << "] ";
	}
	if (!title.empty()) {
		ss << "TITLE:[" << title << "] ";
	}
	if (!msg.empty()) {
		ss << "MSG:[" << msg << "] ";
	}
	return ss.str();
}

bool defLogHandler(LogMsg logMsg) {
	cout << logMsg.toString() << endl;
	::OutputDebugStringA(logMsg.toString().c_str());
	return true;
}

class LogHandlerNode {
public:
	LogHandlerNode();
public:
	Set<string> tags;
	LogHandler handler;
	LogHandlerNode * next;
	LogHandlerNode * pre;
};

LogHandlerNode::LogHandlerNode() {
	next = 0;
	pre = 0;
}
LogConnection::LogConnection() {
	node = 0;
}

class LogHandlerList: public ThreadLock {
public:
	LogHandlerList() {
		root = new LogHandlerNode();
		root->handler = defLogHandler;
	}
	virtual ~LogHandlerList() {
		AutoLock al(this);
		while (root) {
			LogHandlerNode* next = root->next;
			delete root;
			root = next;
		}
	}
public:
	//push(h);  //all
	//push(h, "A"); //A only
	//push(h, "A", "B");// A/B only
	//push(h, "", "A");//no tag or tag A,  empty string tag MUST be set first1!!
	//push(h, "");// no tag only
	LogConnection push(LogHandler handler) {
		AutoLock al(this);
		LogHandlerNode* n = new LogHandlerNode();
		n->handler = handler;
		n->next = root;
		if (root) {
			root->pre = n;
		}
		root = n;
		LogConnection con;
		con.node = root;
		return con;
	}
	LogConnection push(LogHandler handler, string tag1, string tag2 = "", string tag3 = "", string tag4 = "", string tag5 = "") {
		AutoLock al(this);
		LogHandlerNode* n = new LogHandlerNode();
		n->tags.put(tag1);
		n->tags.put(tag2);
		n->tags.put(tag3);
		n->tags.put(tag4);
		n->tags.put(tag5);
		n->handler = handler;
		n->next = root;
		if (root) {
			root->pre = n;
		}
		root = n;
		LogConnection con;
		con.node = root;
		return con;
	}
	void remove(LogConnection& con) {
		AutoLock al(this);
		if (con.node) {
			LogHandlerNode* p = root;
			while (p) {
				if (p == con.node) {
					if (p == root) {
						root = root->next;
					}
					if (p->pre) {
						p->pre->next = p->next;
					}
					if (p->next) {
						p->next->pre = p->pre;
					}
					delete p;
					con.node = 0;
					return;
				}
				p = p->next;
			}
		}
	}
	void fire(const LogMsg& msg) {
		AutoLock al(this);
		LogHandlerNode* p = root;
		while (p) {
			if (p->tags.empty()) {	//all
				if (p->handler(msg)) {
					return;
				}
			} else {
				if (p->tags.has(msg.tag)) {
					if (p->handler(msg)) {
						return;
					}
				}
			}
			p = p->next;
		}
	}
private:
	LogHandlerNode* root;
};

void LogConnection::disconnect() {
	slog.handlers->remove(*this);
	node = 0;
}

MsgField::MsgField() {
	flag = 0;
}
MsgField::MsgField(int flag, string value) {
	this->flag = flag;
	this->value = value;
}

StreamLog::StreamLog() {
	handlers = new LogHandlerList();
}

StreamLog::~StreamLog() {
	if (handlers) {
		delete handlers;
	}
}

LogMsg* StreamLog::getMsg() {
	if (isMainThread()) {
		return &mainMsg;
	}
	int tid = GetCurrentThreadId();
	AutoLock al(this);
	boost::shared_ptr<LogMsg> p = threadMsgs.get(tid);
	if (!p) {
		p.reset(new LogMsg());
		threadMsgs.put(tid, p);
	}
	return p.get();
}

void StreamLog::append(string s) {
	LogMsg& msg = *getMsg();
	msg.msg.append(s);
	//msg.msg = msg.msg + s;
}

void StreamLog::write() {
	LogMsg& msg = *getMsg();
	msg.setTimePidTid();
	handlers->fire(msg);
	msg.init();
}

LogConnection StreamLog::pushLogHandler(LogHandler handler) {
	return handlers->push(handler);
}
LogConnection StreamLog::pushLogHandler(LogHandler handler, string tag1, string tag2, string tag3, string tag4, string tag5 ) {
	return handlers->push(handler, tag1, tag2, tag3, tag4, tag5);
}

void StreamLog::setField(MsgField field) {
	LogMsg& msg = *getMsg();
	if (field.flag == SLOG_TAG) {
		msg.tag = field.value;
	} else if (field.flag == SLOG_TITLE) {
		msg.title = field.value;
	} else {
		msg.msg = field.value;
	}
}

void StreamLog::setLevel(LogLevel level) {
	LogMsg& msg = *getMsg();
	msg.level = level;
}

StreamLog slog;

StreamLog& operator <<(StreamLog& slog, StreamLog& (*fun)(StreamLog&)) {
	return (*fun)(slog);
}
StreamLog& operator <<(StreamLog& slog, MsgField& msgField) {
	slog.setField(msgField);
	return slog;
}

MsgField tag(string s) {
	return MsgField(SLOG_TAG, s);
}

MsgField msg(string s) {
	return MsgField(SLOG_MSG, s);
}

MsgField title(string s) {
	return MsgField(SLOG_TITLE, s);
}

StreamLog& end(StreamLog& slog) {
	slog.write();
	return slog;
}
StreamLog& endln(StreamLog& slog) {
	slog.append("\n");
	slog.write();
	return slog;
}

StreamLog& space(StreamLog& slog) {
	slog.append(" ");
	return slog;
}
StreamLog& sp(StreamLog& slog) {
	slog.append(" ");
	return slog;
}
StreamLog& tab(StreamLog& slog) {
	slog.append("\t");
	return slog;
}

StreamLog& verbose(StreamLog& slog) {
	slog.setLevel(logVerbose);
	return slog;
}
StreamLog& verb(StreamLog& slog) {
	slog.setLevel(logVerbose);
	return slog;
}
StreamLog& info(StreamLog& slog) {
	slog.setLevel(logInfo);
	return slog;
}
StreamLog& err(StreamLog& slog) {
	slog.setLevel(logError);
	return slog;
}
StreamLog& error(StreamLog& slog) {
	slog.setLevel(logError);
	return slog;
}
StreamLog& warn(StreamLog& slog) {
	slog.setLevel(logWarn);
	return slog;
}
StreamLog& dbg(StreamLog& slog) {
	slog.setLevel(logDebug);
	return slog;
}
StreamLog& debug(StreamLog& slog) {
	slog.setLevel(logDebug);
	return slog;
}

StreamLog& operator <<(StreamLog& slog, bool val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, char val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, unsigned char val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, short val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, unsigned short val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, wchar_t val) {
	string s = encodeACP(&val, 1);
	slog.append(s);
	return slog;
}
StreamLog& operator <<(StreamLog& slog, int val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, unsigned int val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, long val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, unsigned long val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}

StreamLog& operator <<(StreamLog& slog, float val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, double val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}
StreamLog& operator <<(StreamLog& slog, long double val) {
	slog.append(boost::lexical_cast<string>(val));
	return slog;
}

StreamLog& operator <<(StreamLog& slog, const char* s) {
	slog.append(s);
	return slog;
}
StreamLog& operator <<(StreamLog& slog, const string& s) {
	slog.append(s);
	return slog;
}

StreamLog& operator <<(StreamLog& slog, const wchar_t* ws) {
	string s = encodeACP(ws, wcslen(ws));
	slog.append(s);
	return slog;
}
StreamLog& operator <<(StreamLog& slog, const wstring& ws) {
	string s = encodeACP(ws.c_str(), ws.length());
	slog.append(s);
	return slog;
}

} /* namespace yet */
